package models

import (
	"errors"
	"fmt"
	"gitee.com/kudingc/role_proxy/common"
	"github.com/astaxie/beego/orm"
	"strconv"
	"time"
)

// 角色与菜单权限相关操作

type SysMenuTree struct {
	Id       int            `json:"id"`
	Name     string         `json:"name"`
	Url      *string        `json:"url"`
	ParentId int            `json:"parent_id"`
	Sort     int            `json:"sort"`
	ApiType  ApiType        `json:"api_type"`
	Children []*SysMenuTree `json:"children" gorm:"-"`
}

type AddRole struct {
	RoleName string     `json:"role_name"`
	MenuData []MenuData `json:"menu_data"`
}

type UpdateRole struct {
	RoleId   int        `json:"role_id"`
	RoleName string     `json:"role_name"`
	State    int        `json:"state"`
	MenuData []MenuData `json:"menu_data"`
}

type RoleMenus struct {
	RoleId   int        `json:"role_id"`
	MenuData []MenuData `json:"menu_data"`
}

type MenuData struct {
	MenuId  int     `json:"menu_id"`
	ApiType ApiType `json:"api_type"`
}

type ApiType int

const (
	// View 查看类api
	View ApiType = iota
	// Operate 操作类api，选择Operate 则自动包含View的权限
	Operate
)

// AddRole 新增角色并授权
func (u *SysRoleMenu) AddRole(addRole AddRole, userId int) (err error) {
	o := orm.NewOrm()
	o.Begin()

	// 插入角色信息
	sysRole := SysRole{
		Name:       addRole.RoleName,
		UserId:     userId,
		CreateTime: time.Now().Format("2006-01-02 15:04:05"),
		State:      1,
	}
	_, err = o.Insert(&sysRole)
	if err != nil || sysRole.Id == 0 {
		o.Rollback()
		return
	}

	// 插入权限信息
	err = updateRoleMenu(sysRole.Id, addRole.MenuData, o)
	if err != nil {
		o.Rollback()
	}
	o.Commit()

	// 重新加载policy信息
	err = common.Enforcer.LoadPolicy()
	return
}

// UpdateRole 修改角色及权限
func (u *SysRoleMenu) UpdateRole(updateRole UpdateRole, userId int) (err error) {
	o := orm.NewOrm()
	o.Begin()

	// 修改角色信息
	sysRole := SysRole{
		Id:         updateRole.RoleId,
		Name:       updateRole.RoleName,
		UserId:     userId,
		CreateTime: time.Now().Format("2006-01-02 15:04:05"),
		State:      updateRole.State,
	}
	_, err = o.Update(&sysRole)
	if err != nil {
		o.Rollback()
		return
	}

	// 修改权限信息
	err = updateRoleMenu(sysRole.Id, updateRole.MenuData, o)
	if err != nil {
		o.Rollback()
	}
	o.Commit()

	// 重新加载policy信息
	err = common.Enforcer.LoadPolicy()
	return
}

// GetRoleData 获取角色信息
func (u *SysRoleMenu) GetRoleData(roleId int) (sysRole SysRole, err error) {
	err = common.Db.QueryTable("sys_role").Filter("id__exact", roleId).One(&sysRole)
	return
}

// GetRoleList 获取角色列表
func (u *SysRoleMenu) GetRoleList() (sysRoles []SysRole, err error) {
	_, err = common.Db.QueryTable("sys_role").All(&sysRoles)
	return
}

func (u *SysRoleMenu) GetRoleMenuDataList(roleId int) (roleMenuData struct {
	SysRole
	MenuTree []*SysMenuTree
}, err error) {
	// 1.获取角色信息
	roleMenuData.SysRole, err = u.GetRoleData(roleId)
	if err != nil {
		return
	}
	// 2.获取权限信息
	roleMenuData.MenuTree, err = u.GetMenuTree(strconv.Itoa(roleId))
	return
}

// GetMenuTree 获取角色指定菜单列表
func (u *SysRoleMenu) GetMenuTree(roleIds string) (menuTree []*SysMenuTree, err error) {

	var menuList []struct {
		SysMenu     SysMenu
		MenuApiType ApiType
	}
	var menuResponse map[int]*SysMenuTree

	menuResponse = make(map[int]*SysMenuTree)

	// 获取权限信息
	sql := "select distinct sm.*, arm.menu_api_type from sys_role_menu arm, sys_menu sm where find_in_set(arm.role_id, ?) and arm.menu_id = sm.id and sm.state = 1 order by sm.id, sm.parent_id, sm.sort;"
	_, err = common.Db.Raw(sql, roleIds).QueryRows(&menuList)

	// 解析查询到的菜单列表，按层次封装
	for _, menu := range menuList {
		sysMenuResponse := SysMenuTree{
			Id:       menu.SysMenu.Id,
			Name:     menu.SysMenu.Name,
			Url:      menu.SysMenu.Url,
			ParentId: menu.SysMenu.ParentId,
			Sort:     menu.SysMenu.Sort,
			ApiType:  menu.MenuApiType,
			Children: []*SysMenuTree{},
		}
		menuResponse[menu.SysMenu.Id] = &sysMenuResponse
		if menu.SysMenu.ParentId != 0 {
			if menuResponse[menu.SysMenu.ParentId] == nil {
				continue
			}
			menuResponse[menu.SysMenu.ParentId].Children = append(menuResponse[menu.SysMenu.ParentId].Children, &sysMenuResponse)
		} else {
			menuTree = append(menuTree, menuResponse[menu.SysMenu.Id])
		}
	}

	return menuTree, err
}

func (u *SysRoleMenu) UpdateRoleMenus(roleId int, menuDatas []MenuData) (err error) {
	o := orm.NewOrm()
	o.Begin()

	err = updateRoleMenu(roleId, menuDatas, o)
	if err != nil {
		o.Rollback()
	}
	o.Commit()
	// 重新加载policy信息
	err = common.Enforcer.LoadPolicy()
	return
}

func updateRoleMenu(roleId int, menuDatas []MenuData, o orm.Ormer) (err error) {
	var sysRoleMenuList []SysRoleMenu
	var selectedApi string

	if o == nil {
		return errors.New("数据库连接失败！")
	}

	if menuDatas == nil {
		return
	}

	// 1.删除原有角色的menu信息
	_, err = o.QueryTable("sys_role_menu").Filter("role_id__exact", roleId).Delete()
	if err != nil {
		return
	}

	// 2.插入角色新的menu信息
	if len(menuDatas) != 0 {
		for _, menuData := range menuDatas {
			sysRoleMenuList = append(sysRoleMenuList, SysRoleMenu{
				RoleId:      roleId,
				MenuId:      menuData.MenuId,
				MenuApiType: menuData.ApiType,
			})
			if menuData.ApiType == Operate {
				selectedApi += fmt.Sprintf("%d-%d", menuData.MenuId, menuData.ApiType) + ","
			}
			selectedApi += fmt.Sprintf("%d-%d", menuData.MenuId, View) + ","
		}

		_, err = o.InsertMulti(len(sysRoleMenuList), &sysRoleMenuList)
		if err != nil {
			return
		}
	}

	// 3.删除原有角色的policy信息
	sql := "delete from casbin_rule where p_type = 'p' and v0 = ?"
	_, err = o.Raw(sql, roleId).Exec()
	if err != nil {
		return
	}

	// 4.新增角色对应的policy信息
	sql = "insert into casbin_rule(p_type,v0,v1,v2) select 'p', srm.role_id, sma.link, sma.act from sys_menu_api sma, sys_role_menu srm where sma.menu_id = srm.menu_id and srm.role_id = ? and find_in_set(concat_ws('-', sma.menu_id, sma.type), ?);"
	_, err = o.Raw(sql, roleId, selectedApi).Exec()
	if err != nil {
		return
	}
	return
}
